package com.small.sentinel;

import com.small.common.core.constants.CommonConstants;
import com.small.common.designPattern.behavioral.chainOfResponsibility.slotchain.ProcessorSlot;
import com.small.common.designPattern.behavioral.chainOfResponsibility.slotchain.ProcessorSlotChain;
import com.small.sentinel.context.ContextUtil;
import com.small.sentinel.context.NullContext;
import com.small.sentinel.log.LoggerSpiProvider;
import com.small.sentinel.slotchain.ResourceWrapper;
import com.small.sentinel.slotchain.SlotChainProvider;
import com.small.sentinel.slotchain.StringResourceWrapper;
import com.small.sentinel.slots.SentinelResource;
import com.small.sentinel.slots.block.BlockException;
import org.slf4j.Logger;

import java.util.HashMap;
import java.util.Map;

public class CtSph implements Sph {

    private static final Logger LOGGER = LoggerSpiProvider.getLogger(Sph.class);

    /**
     * 过滤器链数量
     */
    private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap = new HashMap<ResourceWrapper, ProcessorSlotChain>();

    /**
     * 锁
     */
    private static final Object LOCK = new Object();

    @Override
    public Entry entryWithType(String name, int resourceType, EntryType type, int count, Object[] args) throws BlockException {
        StringResourceWrapper resourceWrapper = new StringResourceWrapper(name,type,resourceType);
        return this.entryWith(resourceWrapper,count,args);
    }

    private Entry entryWith(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException {
        Context context = ContextUtil.getContext();

        if (context instanceof NullContext) {
            return new CtEntry(resourceWrapper, null, context);
        }

        if (context == null) {
            //使用默认上下文
            context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
        }

        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);

        if (chain == null){
            return new CtEntry(resourceWrapper, null, context);
        }
        Entry e = new CtEntry(resourceWrapper, chain, context);
        SentinelResource sentinelResource = new SentinelResource.SentinelResourceBuilder(context,resourceWrapper).builder();

        try {
            chain.entry(sentinelResource);
        } catch (BlockException e1) {
            e.exit(count, args);
            throw e1;
        } catch (Throwable e1) {
            LOGGER.info("Sentinel unexpected exception", e1);
        }
        return e;
    }


    /**
     * 创建过滤器链
     * @param resourceWrapper 资源包装器
     * @return
     */
    ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
        ProcessorSlotChain chain = chainMap.get(resourceWrapper);
        if (chain == null) {
            synchronized (LOCK) {
                chain = chainMap.get(resourceWrapper);
                if (chain == null) {
                    // Entry size limit.
                    if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
                        return null;
                    }

                    chain = SlotChainProvider.newSlotChain();
                    Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
                            chainMap.size() + 1);
                    newMap.putAll(chainMap);
                    newMap.put(resourceWrapper, chain);
                    chainMap = newMap;
                }
            }
        }
        return chain;
    }


    /**
     * 此类用于跳过上下文名称检查。
     */
    private final static class InternalContextUtil extends ContextUtil {
        static Context internalEnter(String name) {
            return trueEnter(name, CommonConstants.NULL);
        }
    }
}
